# Do not change any line in writeResults and haversine functions
import math
def writeResults(results):
    outfile = open("walkable.txt", "w")
    string = "Location of the most walkable house: "+ str(results[0]) + ", " + str(results[1])
    print(string)
    outfile.write(string+"\n")

    string = "Walkability Score: " + str(results[2])
    print(string)
    outfile.write(string+"\n")

    TYPES = ['','','','school', 'fast food', 'post office', 'hospital']
    for i in range(3,len(results)):
        string = "The closest " + TYPES[i] + " is " + str(results[i]) +" km"
        print(string)
        outfile.write(string+"\n")

    outfile.close()


# using function from http://stackoverflow.com/questions/4913349/haversine-formula-in-python-bearing-and-distance-between-two-gps-points
def haversine(point1, point2):
    """
    Calculate the great circle distance between two points
    on the earth (specified in decimal degrees)
    """
    lon1, lat1, lon2, lat2 = point1[1],point1[0],point2[1],point2[0]
    # convert decimal degrees to radians
    lon1, lat1, lon2, lat2 = map(math.radians, [lon1, lat1, lon2, lat2])

    # haversine formula
    dlon = lon2 - lon1
    dlat = lat2 - lat1
    a = math.sin(dlat/2)**2 + math.cos(lat1) *  math.cos(lat2) * math.sin(dlon/2)**2
    c = 2 * math.asin(math.sqrt(a))
    r = 6371 # Radius of earth in kilometers.
    return c * r

window = input("Enter the window: ")
################################################################
# DO NOT CHANGE ANYTHING ABOVE THIS LINE. At the end, call writeResults(results)
# where results is the name of the list that stores the latitude, longitude
# and other stats for the most walkable house as described in the
# assignment description.
################################################################

def extract_info():
    # 从文本中提取数据
    with open('POI.txt','r')as f:
        list_info = f.readlines()

    return list_info

def check_house(location,info):
    '''检查在输入的 window 范围内是否有合适的房子，如果有则以字典格式返回所有的满足条件的房子，
        没有则返回 none

    location - 输入的 window 范围，类似 '47.701460:-122.573313:47.916884:-122.397654'
    info - 从文本中提取的所有数据

    '''
    data = split_data(location)
    if not data:
        return None

    loc1_lati = data[0]
    loc2_lati = data[2]

    loc1_longti = data[1]
    loc2_longti = data[3]

    house = {}
    count = 0

    # 遍历所有信息
    for row in info:
        data = split_data(row)
        if not data:
            continue

        lati = data[1]
        longti = data[2]
        loc_type = data[3].strip()

        # 满足  house 并且 经纬度在给定范围内就插入一条数据
        # alkability Score
        if loc_type == 'house':
            if loc1_lati < lati < loc2_lati or loc1_lati > lati > loc2_lati:
                if loc1_longti < longti < loc2_longti or loc1_longti > longti > loc2_longti:
                    # 满足筛选条件则初始化一条数据，[0 for i in range(7)] 中分别表示
                    # [latitude,longtidude,walkability Score,closest school,closest fast food,closest post office,closest hospital]
                    house['house'+str(count)] = [0 for i in range(7)]
                    house['house'+str(count)][0] = lati
                    house['house'+str(count)][1] = longti
                    count += 1

    return house


def split_data(location):
    '''分离输入的或者文本中的数据,添加错误处理

    >>>split_data('47.701460:-122.573313:47.916884:-122.397654')
    [47.701460,-122.573313,47.916884,-122.397654]
    >>>split_data('1:47.068818:-122.89045:house')
    [1,47.068818,-122.89045,house]

    '''
    data = location.split(':')
    if data and len(data)==4:
        return data
    else:
        return None

def ranks(houses,info):
    '''为所有房子找出 最短的 school,fast food,post office,hospital 路径

    houses - check_house(location,info) 的结果，所有满足条件的房子
    这样的结构：{'house0': ['47.799613', '-122.513552', 0, 0, 0, 0, 0], 'house1': ['47.911731', '-122.528508', 0, 0, 0, 0, 0]}
    列表数据分别为：[latitude,longtidude,walkability Score,closest school,closest fast food,closest post office,closest hospital]

    info - 从文件中提取所有的数据

    '''

    # 遍历所有的数据
    for row in info:
        data = split_data(row)
        if not data:
            continue

        lati = float(data[1])
        longti = float(data[2])
        point1 = [lati,longti]
        loc_type = data[3].strip()
        if loc_type == 'house':
            continue

        # 遍历所有的房子
        for key,value in houses.items():
            c_school = value[3]
            c_ffood = value[4]
            c_poffice = value[5]
            c_hospital = value[6]
            point2 = list(map(float,[value[0],value[1]]))

            # 当有更短的路径出现时，就更新
            if loc_type == 'school':
                # school = caculate(lati,longti,value[0],value[1])
                school = haversine(point1,point2)
                if school < c_school or c_school == 0:
                    value[3] = school

            elif loc_type == 'fast food':
                # ffood = caculate(lati,longti,value[0],value[1])
                ffood = haversine(point1,point2)
                if ffood < c_ffood or c_ffood == 0:
                    value[4] = ffood

            elif loc_type == 'post office':
                # poffice = caculate(lati,longti,value[0],value[1])
                poffice = haversine(point1,point2)
                if poffice < c_poffice or c_poffice == 0:
                    value[5] = poffice

            elif loc_type == 'hospital':
                # hospital = caculate(lati,longti,value[0],value[1])
                hospital = haversine(point1,point2)
                if hospital < c_hospital or c_hospital == 0:
                    value[6] = hospital

            houses[key] = value

    # print(houses)
    return houses

'''弃用
def caculate(lati,longti,loc_lati,loc_longti):
    # 计算两经纬度之间的距离
    lati,longti,loc_lati,loc_longti =  map(float, [lati,longti,loc_lati,loc_longti])
    lati,longti,loc_lati,loc_longti =  map(math.radians, [lati,longti,loc_lati,loc_longti])
    ele1 = math.sin((lati-loc_lati)/2)**2
    ele2 = math.cos(lati)*math.cos(loc_lati)
    ele3 = math.sin((longti-loc_longti)/2)**2
    ele4 = math.sqrt(ele1+ele2*ele3)
    result = 2 * 6371 * math.asin(ele4)
    return result

# print(caculate(47.068818,-122.89045,45.513901,-123.065825))
'''

def get_result(houses):
    '''获x得最终的结果，遍历所有 ranks(houses,info) 的结果，找出总路径最短的房子

    houses - ranks(houses,info) 结果
    数据结构 ： {
    'house0': ['47.799613', '-122.513552', 0, 0.47873973942440085, 0.8803163311643697, 0.8803163311643697, 11.313446252703221],
     'house1': ['47.911731', '-122.528508', 0, 11.51818906203288, 12.355665353845895, 10.88110267934981, 18.93069236996647]
     }


    '''
    # 预设一个最小的结果，当 Walkability Score 更小时更新结果
    min_value = [0 for i in range(7)]
    for value in houses.values():
        value[2] = value[3]+value[4]+value[5]+value[6]
        if value[2] < min_value[2] or min_value[2] == 0:
            min_value = value


    return min_value

'''弃用
def save(value):
    # 保存至文件
    with open('house.txt','w')as f:
         f.write('Location of the most walkable house: {0},{1}\n'.format(value[0],value[1]))
         f.write('Walkability Score: {}\n'.format(value[2]))
         f.write('The closest school is {} km\n'.format(value[3]))
         f.write('The closest fast food is {} km\n'.format(value[4]))
         f.write('The closest post office is {} km\n'.format(value[5]))
         f.write('The closest hospital is {} km\n'.format(value[6]))
'''

def main():

    # test
    # window = '47.701460:-122.573313:47.916884:-122.397654'
    # window = '42.3:-124.2:43.3:-123.3'
    # 提取文件数据
    info = extract_info()
    # 提取所有满足要求的房子
    houses = check_house(window,info)
    if not houses:
        print('No house found in the window')
        return
    # 计算满足要求的房子最短的 school,fast food,post office,hospital 路径
    ranked_houses = ranks(houses,info)
    # 找出最短的路径
    result = get_result(ranked_houses)
    # 保存为文件
    writeResults(result)


if __name__ == '__main__':
    main()
